/*******************************************************************************
 ** Name: assembler.cpp                                                       **
 ** Description: The body of the common settings/parameters file.             **
 **                                                                           **
 ** Open Source Initiative (OSI) Approved License                             **
 **                                                                           **
 ** The contents of this file are subject to the terms of the                 **
 ** Common Development and Distribution License, Version 1.0 only             **
 ** (the "License").  You may not use this file except in compliance          **
 ** with the License.                                                         **
 **                                                                           **
 ** You can find a copy of the license in the license.txt within              **
 ** this distribution or at http://www.munixos.net/licensing.                 **
 ** Software distributed under the License is distributed on an "AS IS"       **
 ** basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.           **
 ** See the License for the specific language governing permissions           **
 ** and limitations under the License.                                        **
 **                                                                           **
 ** When distributing Covered Code, include this CDDL header in each          **
 ** file and include the License file at $HOME/license.txt.                   **
 ** If applicable, add the following below this header, with the indicated    **
 ** fields enclosed by brackets "[]" replaced with your own identifying       **
 ** information: Portions Copyright [yyyy] [name of copyright owner]          **
 **                                                                           **
 **                                                                           **
 ** Copyright (c) 2009-2011  Barry Gian James.                                **
 ** All rights reserved.                                                      **
 **                                                                           **
 ** Ref: $HeadURL$
 ******************************************************************************/
// $Id$

#include <stdio.h>
#include <stdlib.h>

#include "assembler.h"
#include "common.h"

void
Assembler::_readint(char &ch, int &n, bool &ok)
{
    do ch = getc(src); while (ch <= ' ');
    bool negative = (ch == '-');
    ok = true;
    n = 0;
    if (ch == '-' || ch == '+') ch = getc(src);
    while (ch > ' ')
    { if (isdigit(ch))
      n = n * 10 + ch - '0';
    else
      ok = false;
    ch = getc(src);
    }
    if (negative) n = -n;
}

void
Assembler::_readmnemonic(char &ch, char * mnemonic)
{
    int i = 0;
    while (ch > ' ')
    {
        if (i <= 2) { mnemonic[i] = toupper(ch); i++; }
        ch = getc(src);
    }
    mnemonic[i] = '\0';
}

void
Assembler::_error(const char * msg, u32 codetop, bool & error)
{
    printf("%s at %d\n",msg,codetop);
    error = true;
}

void
Assembler::_skiplabel(char &ch)
{
    do
    {
        ch = getc(src);
        if (ch == ';') fscanf(src, "%*[^\n]");

    } while( !(feof(src) || isalpha(ch)));
}


Assembler::Assembler(const char * srcfl, MachineEmulator * e) {
    emu = e;
    src = fopen(srcfl,"r");
    if (src == NULL) {
        cout << "Could not open source file." << endl;
        exit(1);
    }
}

bool
Assembler::Assemble(u32 & codetop, u16 & stacktop)
{
    char    ch;
    char    mnemonic[MAX_MNEMONIC_SIZE];
    OpCode  op = OpCode::BAD;
    char    strDelim;
    bool    bHasErrors = false;
    bool    bNumOK;

    cout << "Assembling..." << endl;
    codetop  = 0;
    stacktop = STACK_MEM_MAX - 1;
    do
    {
        _skiplabel(ch);
        if (!feof(src))                      // we must have a line
        {
            _readmnemonic(ch, mnemonic);   // unpack mnemonic
            op = emu->Opcode(mnemonic);    // look it up
            (emu->Stack())[codetop] = int(op);   // store in memory
            switch (op)
            {
            case OpCode::PRS:
                // requires an address field as an entry into the literal pool
                do strDelim = getc(src);          // determine opening quote character
                while (strDelim != '"' && strDelim != '\'' && strDelim != '\n');
                codetop = (codetop + 1) % STACK_MEM_MAX;
                (emu->Stack())[codetop] = stacktop - 1;   // pointer to start of string
                if (strDelim == '\n')
                    _error("Malformed string address", codetop, bHasErrors);
                else                           // stack string in literal pool
                {
                    ch = getc(src);
                    while (ch != strDelim && ch != '\n')
                    { stacktop--; (emu->Stack())[stacktop] = ch; ch = getc(src); }
                }
                stacktop--;
                (emu->Stack())[stacktop] = 0;      // terminate string with nul
                break;
            case OpCode::ADR:
            case OpCode::PUSH:
            case OpCode::DSP:
            case OpCode::BRUN:
            case OpCode::BREQ:                  // all require address field
                codetop = (codetop + 1) % STACK_MEM_MAX;
                if (ch == '\n')                // no field could be found
                    _error("Missing address", codetop, bHasErrors);
                else                           // unpack it and store
                {
                    _readint(ch, (int&)(emu->Stack())[codetop], bNumOK);
                    if (!bNumOK || feof(src))
                        _error("Bad address", codetop, bHasErrors);
                }
                break;
            case OpCode::BAD:                  // unrecognized mnemonic
                fputs(mnemonic, stdout);
                _error("Invalid opcode", codetop, bHasErrors);
                break;
            default: break;                  // no further action needed
          }
          if (ch != '\n') fscanf(src, "%*[^\n]"); // skip comments
          codetop = (codetop + 1) % STACK_MEM_MAX;
        }
    } while (!feof(src));

    for (int i = codetop; i < stacktop; i++) // fill with invalid opcodes
        (emu->Stack())[i] = int(OpCode::BAD);

}

Assembler::~Assembler()
{
    if (src) fclose(src);
    src = NULL;
}

